package com.hp.bon.sgw.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.core.BuzProcessorManager;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.InprogressMessageQueue;
import com.hp.bon.sgw.core.processor.ActiveProcessor;
import com.hp.bon.sgw.core.processor.CongestionMsgProcessor;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWCapabilityProvided;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.util.dcc.DccAvpHelper;
import com.hp.ocs.diameter.DiameterUtils;
import com.hp.ocs.diameter.message.Message;

public class BONNetCtrlService
{
  private static final Logger logger = LoggerFactory.getLogger(BONNetCtrlService.class);
  private static BONNetCtrlService instance;

  public static BONNetCtrlService getInstance()
  {
    if (instance == null) {
      return new BONNetCtrlService();
    }
    return instance;
  }

  public String sendActive()
  {
    com.hp.bon.sgw.Constants.LOG.info("prepair send active message");
    List<String> needActiveCapas = new ArrayList<String>();
    List<Integer> capabilityIds = new ArrayList<Integer>();
    Node node = null;
    boolean needActive = false;
    for (SGWCapabilityProvided capa : ConfigService.capabiltyProvidedMap.values()) {
      node = (Node)ConfigService.id2NodeMap.get(Integer.valueOf(capa.getPeerNodeId()));
      if ((node != null) && (node.getNeedActive() == 1)) {
        needActive = (node.getNodeType() == 4) || (((node.getNodeType() == 3) || (node.getNodeType() == 5)) && (node.getStatus() == 1));
        if (needActive) {
          try {
            needActiveCapas.add(getCapability(capa.getServiceContextID()));
            capabilityIds.add(Integer.valueOf(capa.getId()));
          }
          catch (Exception localException)
          {
          }
        }
      }
    }

    boolean needActiveBonConfSync = SpringContext.getInstance().getConfigService().getBooleanValue("sgw.activebonconfsync", false);
    if (needActiveBonConfSync) {
      needActiveCapas.add(com.hp.bon.security.Constants.BonConfSyncCapabilityPrefix + "SGW" + "@" + ConfigService.realm);
    }

    String result = returnActiveResult(capabilityIds, needActiveCapas, true, false);
    com.hp.bon.sgw.Constants.LOG.info(result);
    return result;
  }

  private String getCapability(String serviceContextId) {
    if (serviceContextId.indexOf('@') != -1) {
      return serviceContextId.substring(0, serviceContextId.indexOf('@')) + "@" + ConfigService.realm;
    }
    return serviceContextId + "@" + ConfigService.realm;
  }

  public String sendDeActive()
  {
    com.hp.bon.sgw.Constants.LOG.info("prepare send deactive message");
    String result = returnActiveResult(null, null, false, false);
    com.hp.bon.sgw.Constants.LOG.info(result);
    return result;
  }

  private String returnActiveResult(List<Integer> capabilityIds, List<String> capabilitys, boolean active, boolean isNode) {
    StringBuilder str = new StringBuilder();
    str.append(active ? "active" : "de-active");
    String serviceCtxID = active ? "Active.Net-Ctrl@001.ctnbc-bon.net" : "De-Active.Net-Ctrl@001.ctnbc-bon.net";

    List<Node> _SRlist = new ArrayList<Node>();
    ConcurrentHashMap<Integer, Node> nodes = ConfigService.id2NodeMap;
    Node node;
    for (Entry<Integer, Node> nodeEntry : nodes.entrySet()) {
      node = (Node)nodeEntry.getValue();
      if ((node != null) && (node.getNodeType() == 1) && (node.getStatus() == 1)) {
        _SRlist.add(node);
      }
    }

    if (_SRlist.isEmpty()) {
      str.append(" failed. send msg error:No active SR found");
      if (!active) {
        SpringContext.getInstance().getPertriBean().setNewStatus(-2);
      }
      return str.toString();
    }

    ActiveProcessor process = new ActiveProcessor();
    process.resetReqSendCount(_SRlist.size());
    for (Node nodeSR : _SRlist) {
      Message msg = new Message();
      if ((capabilityIds != null) && (!capabilityIds.isEmpty())) {
        msg.setStateObject(capabilityIds);
      }
      String sessionID = DccAvpHelper.createNECapabilityRegMessage(msg, capabilitys, serviceCtxID, ConfigService.sgwHostId);
      CallParam newCallParam = new CallParam(sessionID, serviceCtxID, msg, null, null);
      newCallParam.setProcessor(process);
      newCallParam.setToNode(nodeSR);
      InprogressMessageQueue.getInstance().addRequest(newCallParam);
      nodeSR.getChannel().sendRequest(newCallParam);
    }

    long curTime = System.currentTimeMillis();
    int waitSeconds = 30;
    int sendResult = process.getResultStatus();
    while (System.currentTimeMillis() - curTime < waitSeconds * 1000) {
      if ((sendResult = process.getResultStatus()) != 0) {
        break;
      }
    }
    str.append(" operation finished with ");
    if (sendResult == 1) {
      if (!isNode) {
        SpringContext.getInstance().getPertriBean().setNewStatus(active ? 0 : -2);
      }
      else
      {
        try
        {
          if ((capabilitys != null) && (capabilitys.size() > 0))
          {
            for (String capability : capabilitys)
            {
              alarmAbilityStateNotiry(capability, active ? "1" : "0");
            }
          }
        }
        catch (Exception e)
        {
          logger.warn("Send alarm ability state notify to OMC failed:" + e.getMessage(), e);
        }
      }

      str.append("success.");
    } else if (sendResult == -1) {
      if ((active) && (!isNode)) {
        SpringContext.getInstance().getPertriBean().setNewStatus(-2);
      }
      str.append("failed.");
    } else {
      if ((!active) && (!isNode)) {
        SpringContext.getInstance().getPertriBean().setNewStatus(-2);
      }
      str.append("failed(timeout).");
    }
    return str.toString();
  }

  private void alarmAbilityStateNotiry(String capability, String status)
  {
    logger.warn("Send alarm ability state notify to OMC.[capability=" + capability + " status=" + status + "]");
    try
    {
      OmcRestService.getInstance().getOMCAlarmInterface()
        .alarmAbilityStateNotify(ConfigService.sgwHostId, capability, status, "" + System.currentTimeMillis());
    }
    catch (Exception e)
    {
      logger.warn("Send alarm ability state notify to OMC failed:" + e.getMessage(), e);
    }
  }

  private void alarmNeStateNotify(String neCode, String state)
  {
    logger.warn("Send alarm ne state notify to OMC.[neCode=" + neCode + " state=" + state + "]");
    try
    {
      OmcRestService.getInstance().getOMCAlarmInterface().alarmNeStateNotify(ConfigService.sgwHostId, state, neCode, "" + System.currentTimeMillis());
    }
    catch (Exception e)
    {
      logger.warn("Send alarm ne state notify to OMC failed:" + e.getMessage(), e);
    }
  }

  public String sendNodeActiveOrDeActive(Integer nodeId, boolean active) {
    Node node = (Node)ConfigService.id2NodeMap.get(nodeId);
    if (node == null) {
      return "cannot find Node: id=" + nodeId;
    }
    return sendNodeActiveOrDeActive(node, active);
  }

  public String sendNodeActiveOrDeActive(Node node, boolean active)
  {
    String info = "";
    if (node.getNeedActive() == 1)
    {
      List<String> needCapas = new ArrayList<String>();
      List<Integer> capabilityIds = new ArrayList<Integer>();
      if (active)
        getActiveCapabilitys(node, needCapas, capabilityIds);
      else {
        getDeActiveCapabilitys(node, needCapas, capabilityIds);
      }
      if (needCapas.isEmpty()) {
        info = "[" + node.getHostId() + "] no capability need " + (active ? "active" : "de-active");
        com.hp.bon.sgw.Constants.LOG.info(info);
      } else {
        String result = returnActiveResult(capabilityIds, needCapas, active, true);
        info = "[" + node.getHostId() + (active ? "] active" : "] de-active") + ":" + result;
        com.hp.bon.sgw.Constants.LOG.info(info);
        alarmNeStateNotify(node.getHostId(), active ? "1" : "0");
      }
    }
    else {
      info = "[" + node.getHostId() + "] needActive=" + node.getNeedActive() + ", not need active/de-active";
      com.hp.bon.sgw.Constants.LOG.info(info);
    }
    return info;
  }

  private void getActiveCapabilitys(Node node, List<String> needActiveCapas, List<Integer> capabilityIds) {
    for (SGWCapabilityProvided capa : ConfigService.capabiltyProvidedMap.values())
      if (capa.getPeerNodeId() == node.getId())
        try {
          needActiveCapas.add(getCapability(capa.getServiceContextID()));
          capabilityIds.add(Integer.valueOf(capa.getId()));
        }
        catch (Exception localException)
        {
        }
  }

  private void getDeActiveCapabilitys(Node node, List<String> needActiveCapas, List<Integer> capabilityIds)
  {
    boolean needDeactive = true;
    for (SGWCapabilityProvided capa : ConfigService.capabiltyProvidedMap.values())
      if (capa.getPeerNodeId() == node.getId()) {
        needDeactive = true;
        for (SGWCapabilityProvided capa2 : ConfigService.capabiltyProvidedMap.values()) {
          if ((capa2.getStatus() == 1) && (capa2.getPeerNodeId() != node.getId()))
            needDeactive = false;
        }
        try
        {
          if (needDeactive) {
            needActiveCapas.add(getCapability(capa.getServiceContextID()));
            capabilityIds.add(Integer.valueOf(capa.getId()));
          }
        }
        catch (Exception localException)
        {
        }
      }
  }

  public void notifySystemCrowdedLevel(int level)
  {
    com.hp.bon.sgw.Constants.LOG.info("send Congestion.Net-Ctrl message ...");
    Message msg = new Message();
    String srvCtxID = "Congestion.Net-Ctrl@001.ctnbc-bon.net";
    String sessionID = DiameterUtils.makeSessionID(ConfigService.sgwHostId);
    DccAvpHelper.creatComMessageBeforCCR(msg, sessionID, srvCtxID);
    msg.add(DccAvpHelper.generateCongestionAVP(ConfigService.sgwHostId, level));

    CongestionMsgProcessor process = new CongestionMsgProcessor();
    CallParam newCallParam = new CallParam(sessionID, srvCtxID, msg, null, null);
    newCallParam.setProcessor(process);
    int result = BuzProcessorManager.sendCallParam(newCallParam);
    com.hp.bon.sgw.Constants.LOG.info("send Congestion.Net-Ctrl message:" + result);
  }
}
